/**
 * 生成高斯核
 * @param {*} size
 * @param {*} sigma
 * @returns
 */
export function generateGaussianKernel(size, sigma) {
  const kernel = []
  const center = Math.floor(size / 2)
  let sum = 0
  for (let y = 0; y < size; y++) {
    kernel[y] = []
    for (let x = 0; x < size; x++) {
      const dx = x - center
      const dy = y - center
      const value = Math.exp(-(dx * dx + dy * dy) / (2 * sigma * sigma))
      kernel[y][x] = value
      sum += value
    }
  }
  // 归一化
  for (let y = 0; y < size; y++) {
    for (let x = 0; x < size; x++) {
      kernel[y][x] /= sum
    }
  }
  return kernel
}

/**
 * 应用模糊处理（RGBA 图像）
 * @param {*} imageData
 * @param {*} kernel
 * @returns
 */
export function applyGaussianBlur(imageData, kernel) {
  const canvas = wx.createOffscreenCanvas({type: "2d"})
  const {data, width, height} = imageData
  const output = new Uint8ClampedArray(data.length)
  const kSize = kernel.length
  const kHalf = Math.floor(kSize / 2)

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      let r = 0,
        g = 0,
        b = 0,
        a = 0
      for (let ky = -kHalf; ky <= kHalf; ky++) {
        for (let kx = -kHalf; kx <= kHalf; kx++) {
          const px = Math.min(width - 1, Math.max(0, x + kx))
          const py = Math.min(height - 1, Math.max(0, y + ky))
          const idx = (py * width + px) * 4
          const weight = kernel[ky + kHalf][kx + kHalf]
          r += data[idx] * weight
          g += data[idx + 1] * weight
          b += data[idx + 2] * weight
          a += data[idx + 3] * weight
        }
      }
      const i = (y * width + x) * 4
      output[i] = r
      output[i + 1] = g
      output[i + 2] = b
      output[i + 3] = a
    }
  }

  return canvas.createImageData(output, width, height)
}

/**
 * 计算清晰度
 * @param {*} imageData
 * @returns
 */
export function calculateSharpness(imageData) {
  // 转换为灰度数据
  const grayData = new Uint8Array(imageData.width * imageData.height)
  for (let i = 0, j = 0; i < imageData.data.length; i += 4, j++) {
    grayData[j] = 0.299 * imageData.data[i] + 0.587 * imageData.data[i + 1] + 0.114 * imageData.data[i + 2]
  }

  const {width, height} = imageData

  let sum = 0,
    sum2 = 0,
    N = width * height
  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      let L = 0
      if (x > 0 && x < width - 1 && y > 0 && y < height - 1) {
        L = -4 * grayData[y * width + x] + grayData[y * width + (x - 1)] + grayData[y * width + (x + 1)] + grayData[(y - 1) * width + x] + grayData[(y + 1) * width + x]
      }
      sum += L
      sum2 += L * L
    }
  }
  const mean = sum / N
  return sum2 / N - mean * mean
}

/**
 * 计算亮度
 * @param {*} imageData
 * @returns
 */
export function calculateBrightness(imageData) {
  let totalLuminance = 0

  for (let i = 0; i < imageData.length; i += 4) {
    const r = imageData[i],
      g = imageData[i + 1],
      b = imageData[i + 2]
    const luminance = 0.299 * r + 0.587 * g + 0.114 * b
    totalLuminance += luminance
  }

  return totalLuminance / (imageData.length / 4) // 平均亮度
}

/**
 * 计算二维码坐标
 * @param {*} location
 * @param {*} imageWidth
 * @param {*} imageHeight
 * @returns
 */
export function calculateSkewAngles(location, imageWidth, imageHeight) {
  const tl = location.topLeftCorner
  const tr = location.topRightCorner
  const bl = location.bottomLeftCorner
  const br = location.bottomRightCorner

  const center = {x: imageWidth / 2, y: imageHeight / 2}

  // 1. Calculate rotationRadian (angle of top edge)
  const dxTop = tr.x - tl.x
  const dyTop = tr.y - tl.y
  const rotationRadian = Math.atan2(dyTop, dxTop)

  // Helper: rotate a point around a pivot
  const rotatePoint = (p, angle, pivot) => {
    const s = Math.sin(angle)
    const c = Math.cos(angle)
    const x = p.x - pivot.x
    const y = p.y - pivot.y
    return {
      x: x * c - y * s + pivot.x,
      y: x * s + y * c + pivot.y
    }
  }

  // 2. De-rotate all points relative to image center
  const negRotation = -rotationRadian
  const points = [tl, tr, bl, br]
  const [tl_R, tr_R, bl_R, br_R] = points.map(p => rotatePoint(p, negRotation, center))

  // 3. Translate to relative frame (origin at tl_R)
  const offsetX = tl_R.x
  const offsetY = tl_R.y
  const tr_R_adj = {x: tr_R.x - offsetX, y: tr_R.y - offsetY}
  const bl_R_adj = {x: bl_R.x - offsetX, y: bl_R.y - offsetY}
  const br_R_adj = {x: br_R.x - offsetX, y: br_R.y - offsetY}
  const tl_R_adj = {x: 0, y: 0}

  // 4. Skew angles
  const horizontalSkew = Math.atan2(bl_R_adj.x, bl_R_adj.y)
  const verticalSkew = Math.atan2(br_R_adj.y - bl_R_adj.y, br_R_adj.x - bl_R_adj.x)

  // 5. QR size estimation (from top and left edges)
  const cosVSkew = Math.cos(verticalSkew)
  const cosHSkew = Math.cos(horizontalSkew)
  let qrSize

  if (cosVSkew > 0.0001 && cosHSkew > 0.0001) {
    const s1 = tr_R_adj.x / cosVSkew
    const s2 = bl_R_adj.y / cosHSkew
    qrSize = (Math.abs(s1) + Math.abs(s2)) / 2
  } else if (cosHSkew > 0.0001) {
    qrSize = Math.abs(bl_R_adj.y / cosHSkew)
  } else if (cosVSkew > 0.0001) {
    qrSize = Math.abs(tr_R_adj.x / cosVSkew)
  } else {
    qrSize = (Math.abs(tr_R_adj.x) + Math.abs(bl_R_adj.y)) / 2
  }

  const symmetricWidth1 = Math.abs(br_R_adj.x - tl_R_adj.x) // topLeft ↔ bottomRight
  const symmetricWidth2 = Math.abs(bl_R_adj.x - tr_R_adj.x) // topRight ↔ bottomLeft
  const maxSymmetricWidth = Math.max(symmetricWidth1, symmetricWidth2)

  // 使用对称角宽度作为 qrSize（如果更大）
  if (maxSymmetricWidth > qrSize) {
    qrSize = maxSymmetricWidth
  }

  // 6. maxTopLeft: bounding box origin in original coordinates
  const minX = Math.min(tl_R_adj.x, tr_R_adj.x, bl_R_adj.x, br_R_adj.x)
  const minY = Math.min(tl_R_adj.y, tr_R_adj.y, bl_R_adj.y, br_R_adj.y)
  const minVec = {x: minX + offsetX, y: minY + offsetY}
  const maxTopLeft = rotatePoint(minVec, rotationRadian, center)

  // 7. correctedTopLeft: in de-rotated frame relative to original tl
  const correctedTopLeft = {
    x: tl_R.x + minX,
    y: tl_R.y + minY
  }

  return {
    rotationRadian,
    verticalSkew: verticalSkew * (180 / Math.PI),
    horizontalSkew: horizontalSkew * (180 / Math.PI),
    qrSize,
    maxTopLeft,
    correctedTopLeft
  }
}
